<?php
namespace PsiFramework\Classes\Factory;

use PsiFramework\Classes\Util\Parameters\Object\AttributeParameters;

use PsiFramework\Classes\Util\Parameters\ObjectParameters;

use PsiFramework\Classes\Model\Object;

use PsiFramework\Classes\Model\ObjectProxy;

import("Classes.Util.Manager.ApplicationManager");

use \PsiFramework\Classes\Util\Manager\ApplicationManager;

/**
 * Bibliotheque
 * Gestion de ma bibliothèque
 *
 * @author Rémi San
 * @version beta
 */


/**
 * The ObjectFactory is used to get the correct Object instance
 *
 * @package Factories
 */
class ObjectFactory
{

    /**
     * Static function to retrieve a new instance of
     * the object based on its name
     *
     * @param string $objectName the object name
     * 
     * @return Object the wanted Object
     */
    public static function getNewObject(
        /*string*/ $objectName,
        /*bool*/ $qualified=false
    )
    {
        $class = self::importObject($objectName, $qualified);
        $class = getClassName($class);
        if (!class_exists($class)) {
            return null;
        }
        $obj = new $class();
        $obj->setObjectName($objectName);
        return $obj ;
    }
    
    /**
     * Copy an object
     * 
     * @param Object $object
     * @param bool $withLinks
     * 
     * @throws \Exception
     * @return Object
     */
    public static function copy(Object $object, /*bool*/ $withLinks=false)
    {
        $objectName = $object->getObjectName();
        
        if (isNullOrEmptyString($objectName)) {
            throw new \Exception('Can\'t copy an object without a type !');
        }
        
        $dao = DAOFactory::getDAO($objectName);
        $newObject = self::getNewObject($objectName);
        
        $objectParameters = self::getObjectParameters($objectName);
        
        //The new object is created
        $savedParams = array();
        $params = $objectParameters->getParameters();
        foreach ($params as $param) {
            /* @var $param AttributeParameters */
            $p = $object->{$param->getter}();
            
            if ($param->isArray) {
                $savedParams[$param->name] = $p;
            } else {
                if ($param->unique) {
                    if ($param->type == 'string') {
                        $p .= 'copy_('.uniqid().')';
                    } else {
                        throw new \Exception('Can\'t copy an object with unique params wich are not strings !');
                    }
                }
                
                $newObject->{$param->setter}($p);
            }
        }
        $newObject->setId(null);
        $dao->prepareForCopy($newObject);
        
        $dao->save($newObject);
        
        //If the linked objects must be copied too
        if ($withLinks) {
            foreach ($savedParams as $paramName=>$paramValue) {
                $param = $objectParameters->getParameter($paramName);
                $type = $param->type;
                
                $newList = array();
                
                if (count($paramValue)>0 && !in_array($paramName, $dao->getNoCopyParametersList())) {
                    
                    $subObjectParameters = self::getObjectParameters($type);
                    $subDao = DAOFactory::getDAO($type);
                    
                    //Retrieve the correct setter
                    $outSetter = null;
                    foreach ($subObjectParameters->getParameters() as $subParam) {
                        /* @var $subParam AttributeParameters */
                        if ($subParam->type == $objectName) {
                            $outSetter = $subParam->setter;
                        }
                    }
                    
                    //Creates the new links
                    foreach ($paramValue as $arrayValue) {
                        $newListElement = clone $arrayValue;
                        $newListElement->{$outSetter}($newObject);
                        $newList[] = $newListElement;
                        $subDao->save($newListElement);
                    }
                }
                
                $newObject->{$param->setter}($newList);
            }
        }
        
        return $newObject;
    }

    /**
     * Returns the ObjectParameters for the specified name
     * 
     * @param string $objectName
     * 
     * @return ObjectParameters
     */
    protected static function getObjectParameters(/*string*/ $objectName)
    {
        $applicationManager = ApplicationManager::getInstance();
        $application = $applicationManager->getApplication();
        $objectParameters = $application->getObjectParameter($objectName);
        return $objectParameters;
    }
    
    /**
     * Static function to import the class of an object
     *
     * @param string $objectName    the object name
     * @return mixed    $object        the wanted Object
     */
    public static function importObject(
        /*string*/ $objectName,
        /*bool*/ $qualified=false
    )
    {
        $class = null ;
        if (!$qualified) {
            $objectParameters = self::getObjectParameters($objectName);
            if ($objectParameters == null) {
                return null ;
            }
            $class = $objectParameters->getClass();
        } else {
            $class = $objectName ;
        }

        if (is_null($class)) {
            return null ;
        }

        import($class);
        return $class ;
    }
}
